package com.hb.web;

import com.hb.data.SpitterRepository;
import com.hb.model.Spitter;
import com.hb.model.SpitterForm;
import com.hb.model.Spittle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.Part;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping("/spitter")
public class SpitterController {
    private SpitterRepository spitterRepository;

    public SpitterController() {
    }

    @Autowired
    public SpitterController(SpitterRepository spitterRepository) {
        this.spitterRepository = spitterRepository;
    }

    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public String showRegistrationForm(Model model) {
        model.addAttribute(new Spitter());
        return "registerForm";
    }

    /**
     * 1、实现控制器方法接收上传的文件最常见的方式就是在某个控制器方法参数上添加 @RequestPart 注解，并使用 byte 数组参数接收上传的图片
     * 2、当注册表单提交的时候，profilePicture 属性将会给定一个 byte 数组，这个数组中包含了请求中对应 part 的数据（通过 @RequestPart 指定）。
     * 如果用户提交表单的时候没有选择文件，那么这个数组会是空（而不是 null）。获取到图片数据后，processRegistration() 方法剩下的任务就是将文件保存到某个位置。
     *
     * @param profilePicture
     * @param spitter
     * @param errors
     * @return
     */
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, @Valid Spitter spitter, Errors errors) {
        if (errors.hasErrors()) {
            return "registerForm";
        }

        spitterRepository.save(spitter);
        // todo: 将存有图片数据的字节数组作为图片的形式保存到固定位置上
        return "redirect:/spitter/" + spitter.getUsername();
    }

    /**
     * 使用上传文件的原始 byte 比较简单但是功能有限。因此，Spring 还提供了 MultipartFile 接口，它为处理 multipart 数据提供了内容更为丰富的对象。
     * MultipartFile 提供了获取上传文件 byte 的方式，但是它所提供的功能并不仅限于此，还能获得原始的文件名、大小以及内容类型。它还提供了一个 InputStream，
     * 用来将文件数据以流的方式进行读取。除此之外，MultipartFile 还提供了一个便利的 transferTo() 方法，它能够帮助我们将上传的文件写入到文件系统中。
     *
     * @param spitterForm
     * @param errors
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "register1", method = RequestMethod.POST)
    public String processRegistration1(@Valid SpitterForm spitterForm, Errors errors) throws IOException {
        if (errors.hasErrors()) {
            return "registerForm";
        }

        Spitter spitter = spitterForm.toSpitter();
//        spitterRepository.save(spitter);
        MultipartFile multipartFile = spitterForm.getProfilePicture();
        multipartFile.transferTo(new File("/" + multipartFile.getOriginalFilename()));
        return "redirect:/spitter/" + spitter.getUsername();
    }

    /**
     * 1、如果你需要将应用部署到 Servlet 3.0 的容器中，那么会有 MultipartFile 的一个替代方案。Spring MVC 也能接受 javax.servlet.http.Part 作为控制器方法的参数。
     * Part 接口与 MultipartFile 并没有太大的差别，可以看到 Part 接口的有一些方法其实是与 MultipartFile 相对应的。其中要注意的是 getSubmittedFileName() 对应于
     * getOriginalFilename()。类似地，write() 对应于 transferTo()。
     * 2、值得一提的是，如果在编写控制器方法的时候，通过 Part 参数的形式接受文件上传，那么就没有必要配置 MultipartResolver 了。只有使用 MultipartFile 的时候，
     * 我们才需要 MultipartResolver。
     * @param profilePicture
     * @param spitter
     * @param errors
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "register2", method = RequestMethod.POST)
    public String processRegistration2(@RequestPart("profilePicture") Part profilePicture, @Valid Spitter spitter, Errors errors) throws IOException {
        if (errors.hasErrors()) {
            return "registerForm";
        }

//        spitterRepository.save(spitter);
        profilePicture.write("/tmp/spittr/uploads/" + profilePicture.getSubmittedFileName());
        return "redirect:/spitter/" + spitter.getUsername();
    }

    @RequestMapping(value = "/register3", method = RequestMethod.POST)
    public String processRegistration3(Spitter spitter, Model model) {
        spitterRepository.save(spitter);

        model.addAttribute("username", spitter.getUsername());
        // 除了连接 String 的方式来构建重定向 URL，Spring 还提供了使用模板的方式来定义重定向 URL。
        // 现在，username 作为占位符填充到了 URL 模板中，而不是直接连接到重定向 String 中，所以 username 中所有的不安全字符都会进行
        // 转义。这样会更加安全，这里允许用户输入任何想要的内容作为 username，并会将其附加到路径上。
        return "redirect:/spitter/{username}";
    }

    @RequestMapping(value = "/register4", method = RequestMethod.POST)
    public String processRegistration4(Spitter spitter, Model model) {
        spitterRepository.save(spitter);

        model.addAttribute("username", spitter.getUsername());
        model.addAttribute("spitterId", spitter.getId());
        // 模型中所有其他的原始类型值都可以添加到 URL 中作为查询参数。作为样例，假设除了 username 以外，模型中还要包含新创建 Spitter 对象的 id 属性，
        // 所返回的重定向 String 并没有太大的变化。但是，因为模型中的 spitterId 属性没有匹配重定向 URL 中的任何占位符，所以它会自动以查询参数的形式
        // 附加到重定向 URL 上。如果 username 属性的值是 habuma 并且 spitterId 属性的值是 42，那么结果得到的重定向URL路径将会是
        // “/spitter/habuma?spitterId=42”。
        return "redirect:/spitter/{username}";
    }

    @RequestMapping(value = "/register5", method = RequestMethod.POST)
    public String processRegistration5(Spitter spitter, RedirectAttributes model) {
        spitterRepository.save(spitter);

        model.addAttribute("username", spitter.getUsername());
        // RedirectAttributes 提供了一组 addFlashAttribute() 方法来添加 flash 属性
        model.addFlashAttribute("spitter", spitter);
        return "redirect:/spitter/{username}";
    }

    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public String showSpitterProfile(@PathVariable String username, Model model) {
        // Spitter spitter = spitterRepository.findByUsername(username);
        Spitter spitter = new Spitter("admin", "123456789", "li", "wei");
        model.addAttribute(spitter);
        return "profile";
    }

    /**
     * 搭配 flash 属性的方式
     * @param username
     * @param model
     * @return
     */
    @RequestMapping(value = "/sp/{username}", method = RequestMethod.GET)
    public String showSpitterProfile1(@PathVariable String username, Model model) {
        if(!model.containsAttribute("spitter")) {
            model.addAttribute(spitterRepository.findByUsername(username));
        }
        return "profile";
    }
}
